/***************************************************************************
 *   Copyright (C) 2009 - 2010 by Simon Qian <SimonQian@SimonQian.com>     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "vsf.h"
#include "usrapp.h"

struct usrapp_t usrapp;
struct app_hwcfg_t
{
	struct
	{
		struct vsfhal_gpio_pin_t pullup;
	} usbd;
} static const app_hwcfg =
{
	.usbd.pullup.port = USB_PULLUP_PORT,
	.usbd.pullup.pin = USB_PULLUP_PIN,
};

struct usrapp_param_t
{
	struct
	{
		uint8_t DeviceDescriptor[18];
		uint8_t ConfigDescriptor[239];
		uint8_t StringLangID[4];
		uint8_t StringVendor[20];
		uint8_t StringProduct[14];
		uint8_t StringFunc_CDC[14];
		uint8_t StringFunc_UVC[14];
		struct vsfusbd_desc_filter_t StdDesc[8];
	} usbd;
} static const usrapp_param =
{
	.usbd.DeviceDescriptor =
	{
		USB_DT_DEVICE_SIZE,
		USB_DT_DEVICE,
		0x00, 0x02,	// bcdUSB
		0xEF,		// device class: IAD
		0x02,		// device sub class
		0x01,		// device protocol
		64,			// max packet size
		(GENERATE_HEX(APPCFG_USBD_VID) >> 0) & 0xFF,
		(GENERATE_HEX(APPCFG_USBD_VID) >> 8) & 0xFF,
					// vendor
		(GENERATE_HEX(APPCFG_USBD_PID) >> 0) & 0xFF,
		(GENERATE_HEX(APPCFG_USBD_PID) >> 8) & 0xFF,
					// product
		0x00, 0x02,	// bcdDevice
		1,			// manu facturer
		2,			// product
		0,			// serial number
		1,			// number of configuration
	},
	.usbd.ConfigDescriptor =
	{
		USB_DT_CONFIG_SIZE,
		USB_DT_CONFIG,
		(sizeof(usrapp_param.usbd.ConfigDescriptor) >> 0) & 0xFF,
		(sizeof(usrapp_param.usbd.ConfigDescriptor) >> 8) & 0xFF,
					// wTotalLength
		4,			// bNumInterfaces: 4 interfaces
		0x01,		// bConfigurationValue: Configuration value
		0x00,		// iConfiguration: Index of string descriptor describing the configuration
		0x80,		// bmAttributes: bus powered
		0x64,		// MaxPower

		// IDA for CDC
		USB_DT_INTERFACE_ASSOCIATION_SIZE,
		USB_DT_INTERFACE_ASSOCIATION,
		0,			// bFirstInterface
		2,			// bInterfaceCount
		0x02,		// bFunctionClass
		0x02,		// bFunctionSubClass
		0x01,		// bFunctionProtocol
		4,			// iFunction

		USB_DT_INTERFACE_SIZE,
		USB_DT_INTERFACE,
		0,			// bInterfaceNumber: Number of Interface
		0,			// bAlternateSetting: Alternate setting
		1,			// bNumEndpoints
		0x02,		// bInterfaceClass:
		0x02,		// bInterfaceSubClass:
		0x01,		// nInterfaceProtocol:
		4,			// iInterface:

		// Header Functional Descriptor
		5,			// bLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x00,		// bDescriptorSubtype: Header Func Desc
		0x10,		// bcdCDC: spec release number
		0x01,

		// Call Managment Functional Descriptor
		5,			// bLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x01,		// bDescriptorSubtype: Call Management Func Desc
		0x00,		// bmCapabilities: D0+D1
		1,			// bDataInterface: 1

		// ACM Functional Descriptor
		4,			// bLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x02,		// bDescriptorSubtype: Abstract Control Management desc
		0x02,		// bmCapabilities

		// Union Functional Descriptor
		5,			// bLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x06,		// bDescriptorSubtype: Union func desc
		0,			// bMasterInterface: Communication class interface
		1,			// bSlaveInterface0: Data Class Interface

		USB_DT_ENDPOINT_SIZE,
		USB_DT_ENDPOINT,
		0x81,		// bEndpointAddress: IN1
		0x03,		// bmAttributes: Interrupt
		8, 0x00,	// wMaxPacketSize:
#if defined(VSFUSBD_CFG_HIGHSPEED)
		0x10,		// bInterval:
#elif defined(VSFUSBD_CFG_FULLSPEED)
		0xFF,		// bInterval:
#endif

		USB_DT_INTERFACE_SIZE,
		USB_DT_INTERFACE,
		0x01,		// bInterfaceNumber: Number of Interface
		0x00,		// bAlternateSetting: Alternate setting
		2,			// bNumEndpoints
		0x0A,		// bInterfaceClass
		0x00,		// bInterfaceSubClass
		0x00,		// nInterfaceProtocol
		0x04,		// iInterface:

		USB_DT_ENDPOINT_SIZE,
		USB_DT_ENDPOINT,
		0x82,		// bEndpointAddress: IN2
		0x02,		// bmAttributes: Bulk
#if defined(VSFUSBD_CFG_HIGHSPEED)
		0x00, 0x02,	// wMaxPacketSize:
#elif defined(VSFUSBD_CFG_FULLSPEED)
		0x40, 0x00,	// wMaxPacketSize:
#endif
		0x00,		// bInterval:

		USB_DT_ENDPOINT_SIZE,
		USB_DT_ENDPOINT,
		0x02,		// bEndpointAddress: OUT2
		0x02,		// bmAttributes: Bulk
#if defined(VSFUSBD_CFG_HIGHSPEED)
		0x00, 0x02,	// wMaxPacketSize:
#elif defined(VSFUSBD_CFG_FULLSPEED)
		0x40, 0x00,	// wMaxPacketSize:
#endif
		0x00,		// bInterval:

		// IDA for UVC
		USB_DT_INTERFACE_ASSOCIATION_SIZE,
		USB_DT_INTERFACE_ASSOCIATION,
		2,			// bFirstInterface
		2,			// bInterfaceCount
		0x0E,		// bFunctionClass: CC_VIDEO
		0x03,		// bFunctionSubClass: SC_VIDEO_INTERFACE_COLLECTION
		0x00,		// bFunctionProtocol: PC_PROTOCOL_UNDEFINED
		5,			// iFunction: VSFUVC

		USB_DT_INTERFACE_SIZE,
		USB_DT_INTERFACE,
		2,			// bInterfaceNumber: Number of Interface
		0,			// bAlternateSetting: Alternate setting
		1,			// bNumEndpoints
		0x0E,		// bInterfaceClass: CC_VIDEO
		0x01,		// bInterfaceSubClass: SC_VIDEOCONTROL
		0x00,		// nInterfaceProtocol: PC_PROTOCOL_UNDEFINED
		5,			// iInterface: VSFUVC

		13,			// bLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x01,		// bDescriptorSubType: VD_HEADER
		0x50, 0x01,	// bdcUVC: 1.50
		39, 0,		// wTotalLength
		0x80, 0xC3, 0xC9, 0x01,
					// dwClockFrequency: 30M
		1,			// bInCollection
		1,			// baInterfaceNr[bInCollection]

		17,			// bLength
		0x24,		// bDescriptorTpye: CS_INTERFACE
		0x02,		// bDescriptorSubType: VC_INPUT_TERMINAL
		1,			// bTerminalID
		0x01, 0x02,	// wTermianType: ITT_CAMERA
		0,			// bAssocTerminal
		0,			// iTerminal
		0, 0,		// wObjectiveFocalLengthMin
		0, 0,		// wObjectiveFocalLengthMax
		0, 0,		// wOcularFocalLength
		2,			// bControlSize
		0x00, 0x00,	// bmControls

		9,			// bLength
		0x24,		// bDescriptorTpye: CS_INTERFACE
		0x03,		// bDescriptorSubType: VC_OUTPUT_TERMINAL
		2,			// bTerminalID
		0x01, 0x01,	// wTrminalType: TT_STREAMING
		0,			// bAssocTerminal
		1,			// bSourcdID
		0,			// iTerminal

		USB_DT_ENDPOINT_SIZE,
		USB_DT_ENDPOINT,
		0x83,		// bEndpointAddress: IN3
		0x03,		// bmAttributes: Interrupt
		0x40, 0x00,	// wMaxPacketSize:
		0x08,		// bInterval:

		0x05,		// bLength
		0x25,		// bDescriptorType: CS_ENDPOINT
		0x03,		// bDescriptorSubType: EP_INTERRUPT
		0x40, 0x00,	// wMaxTransferSize

		USB_DT_INTERFACE_SIZE,
		USB_DT_INTERFACE,
		3,			// bInterfaceNumber: Number of Interface
		0,			// bAlternateSetting: Alternate setting
		0,			// bNumEndpoints
		0x0E,		// bInterfaceClass: CC_VIDEO
		0x02,		// bInterfaceSubClass: SC_VIDEOSTREAMING
		0x00,		// nInterfaceProtocol: PC_PROTOCOL_UNDEFINED
		0,			// iInterface:

		14,			// bLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x01,		// bDescriptorSubType: VS_INPUT_HEADER
		1,			// bNumFormats
		71, 0,		// wTotalLength
		0x84,		// bEndpointAddress
		0x00,		// bmInfo
		2,			// bTerminalLink
		0x00,		// bStillCaptureMethod
		0x00,		// bTriggerSupport
		0x00,		// bTriggerUsage
		0x01,		// bControlSize
		0x00,		// bmaControls

		27,			// bLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x04,		// bDescriptorSubType: VS_FORMAT_UNCOMPRESSED
		1,			// bFormatIndex
		1,			// bNumFrameDescriptors
		0x59, 0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00,
		0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
					// guidFormat: 
		16,			// bBitsPerPixel
		1,			// bDefaultFrameIndex
		0,			// bAspectRatioX
		0,			// bAspectRatioY
		0x00,		// bmInterlaceFlags
		0,			// bCopyProtect

		30,			// bLength
		0x24,		// bDescriptorType: CS_INTERFACE
		0x05,		// bDescriptorSubType: VS_FRAME_UNCOMPRESSED
		1,			// bFrameIndex
		0x00,		// bmCapabilities
		0x00, 0x01,	// wWidth: 256
		0xF0, 0x00,	// wHeight: 240
		0x00, 0x00, 0x84, 0x03,
					// dwMinBitRate: 256 * 240 * 2 * 60 * 8
		0x00, 0x00, 0x84, 0x03,
					// dwMaxBitRate
		0x00, 0xE0, 0x01, 0x00,
					// dwMaxVideoFrameBufferSize: 256 * 240 * 2
		0x0A, 0x8B, 0x02, 0x00,
					// dwDefaultFrameInterval: 166666ns, 60fps
		1,			// bFrameIntervalType
		0x0A, 0x8B, 0x02, 0x00,
					// dwFrameInterval[0]

		USB_DT_INTERFACE_SIZE,
		USB_DT_INTERFACE,
		3,			// bInterfaceNumber: Number of Interface
		1,			// bAlternateSetting: Alternate setting
		1,			// bNumEndpoints
		0x0E,		// bInterfaceClass: CC_VIDEO
		0x02,		// bInterfaceSubClass: SC_VIDEOSTREAMING
		0x00,		// nInterfaceProtocol: PC_PROTOCOL_UNDEFINED
		0,			// iInterface:

		USB_DT_ENDPOINT_SIZE,
		USB_DT_ENDPOINT,
		0x84,		// bEndpointAddress: IN4
		0x05,		// bmAttributes: Isochronus
		0x00, 0x04,	// wMaxPacketSize:
		0x01,		// bInterval:
	},
	.usbd.StringLangID =
	{
		4,
		USB_DT_STRING,
		0x09,
		0x04,
	},
	.usbd.StringVendor =
	{
		20,
		USB_DT_STRING,
		'S', 0, 'i', 0, 'm', 0, 'o', 0, 'n', 0, 'Q', 0, 'i', 0, 'a', 0,
		'n', 0,
	},
	.usbd.StringProduct =
	{
		14,
		USB_DT_STRING,
		'V', 0, 'S', 0, 'F', 0, 'N', 0, 'E', 0, 'S', 0,
	},
	.usbd.StringFunc_CDC =
	{
		14,
		USB_DT_STRING,
		'V', 0, 'S', 0, 'F', 0, 'C', 0, 'D', 0, 'C', 0,
	},
	.usbd.StringFunc_UVC =
	{
		14,
		USB_DT_STRING,
		'V', 0, 'S', 0, 'F', 0, 'U', 0, 'V', 0, 'C', 0,
	},
	.usbd.StdDesc =
	{
		VSFUSBD_DESC_DEVICE(0, usrapp_param.usbd.DeviceDescriptor, sizeof(usrapp_param.usbd.DeviceDescriptor)),
		VSFUSBD_DESC_CONFIG(0, 0, usrapp_param.usbd.ConfigDescriptor, sizeof(usrapp_param.usbd.ConfigDescriptor)),
		VSFUSBD_DESC_STRING(0, 0, usrapp_param.usbd.StringLangID, sizeof(usrapp_param.usbd.StringLangID)),
		VSFUSBD_DESC_STRING(0x0409, 1, usrapp_param.usbd.StringVendor, sizeof(usrapp_param.usbd.StringVendor)),
		VSFUSBD_DESC_STRING(0x0409, 2, usrapp_param.usbd.StringProduct, sizeof(usrapp_param.usbd.StringProduct)),
		VSFUSBD_DESC_STRING(0x0409, 4, usrapp_param.usbd.StringFunc_CDC, sizeof(usrapp_param.usbd.StringFunc_CDC)),
		VSFUSBD_DESC_STRING(0x0409, 5, usrapp_param.usbd.StringFunc_UVC, sizeof(usrapp_param.usbd.StringFunc_UVC)),
		VSFUSBD_DESC_NULL,
	},
};

struct usrapp_t usrapp =
{
	.hcd_param.index						= VSFHAL_HCD_INDEX,
	.hcd_param.int_priority					= 0xFF,
	.usbh.hcd								= &vsfohci_drv,
	.usbh.hcd.param							= &usrapp.hcd_param,

	.usbd.cdc.param.CDC.ep_notify			= 1,
	.usbd.cdc.param.CDC.ep_out				= 2,
	.usbd.cdc.param.CDC.ep_in				= 2,
	.usbd.cdc.param.CDC.stream_tx			= (struct vsf_stream_t *)&usrapp.usbd.cdc.stream_tx,
	.usbd.cdc.param.CDC.stream_rx			= (struct vsf_stream_t *)&usrapp.usbd.cdc.stream_rx,
	.usbd.cdc.param.line_coding.bitrate		= 115200,
	.usbd.cdc.param.line_coding.stopbittype	= 0,
	.usbd.cdc.param.line_coding.paritytype	= 0,
	.usbd.cdc.param.line_coding.datatype	= 8,
	.usbd.cdc.stream_tx.stream.op			= &vsf_fifostream_op,
	.usbd.cdc.stream_tx.mem.buffer.buffer	= (uint8_t *)&usrapp.usbd.cdc.txbuff,
	.usbd.cdc.stream_tx.mem.buffer.size		= sizeof(usrapp.usbd.cdc.txbuff),
	.usbd.cdc.stream_rx.stream.op			= &vsf_fifostream_op,
	.usbd.cdc.stream_rx.mem.buffer.buffer	= (uint8_t *)&usrapp.usbd.cdc.rxbuff,
	.usbd.cdc.stream_rx.mem.buffer.size		= sizeof(usrapp.usbd.cdc.rxbuff),

	.usbd.uvc.param.ep_in					= 4,

	.usbd.ifaces[0].class_protocol			= (struct vsfusbd_class_protocol_t *)&vsfusbd_CDCACMControl_class,
	.usbd.ifaces[0].protocol_param			= &usrapp.usbd.cdc.param,
	.usbd.ifaces[1].class_protocol			= (struct vsfusbd_class_protocol_t *)&vsfusbd_CDCACMData_class,
	.usbd.ifaces[1].protocol_param			= &usrapp.usbd.cdc.param,
	.usbd.ifaces[2].class_protocol			= (struct vsfusbd_class_protocol_t *)&vsfusbd_UVCControl_class,
	.usbd.ifaces[2].protocol_param			= &usrapp.usbd.uvc.param,
	.usbd.ifaces[3].class_protocol			= (struct vsfusbd_class_protocol_t *)&vsfusbd_UVCData_class,
	.usbd.ifaces[3].protocol_param			= &usrapp.usbd.uvc.param,
	.usbd.config[0].num_of_ifaces			= dimof(usrapp.usbd.ifaces),
	.usbd.config[0].iface					= usrapp.usbd.ifaces,
	.usbd.device.num_of_configuration		= dimof(usrapp.usbd.config),
	.usbd.device.config						= usrapp.usbd.config,
	.usbd.device.desc_filter				= (struct vsfusbd_desc_filter_t *)usrapp_param.usbd.StdDesc,
	.usbd.device.device_class_iface			= 0,
	.usbd.device.drv						= (struct vsfhal_usbd_t *)&vsfhal_usbd,
	.usbd.device.int_priority				= 0xFF,
};

static void usrapp_usbd_conn(void *p)
{
	struct usrapp_t *app = (struct usrapp_t *)p;

	vsfusbd_device_init(&app->usbd.device);
	app->usbd.device.drv->connect();
	if (app_hwcfg.usbd.pullup.port != VSFHAL_DUMMY_PORT)
		vsfhal_gpio_set(app_hwcfg.usbd.pullup.port, 1 << app_hwcfg.usbd.pullup.pin);
}

void usrapp_srt_init(struct usrapp_t *app)
{
	VSFSTREAM_INIT(&app->usbd.cdc.stream_rx);
	VSFSTREAM_INIT(&app->usbd.cdc.stream_tx);
	vsfdbg_init((struct vsf_stream_t *)&app->usbd.cdc.stream_tx);

	vsfusbh_init(&usrapp.usbh);
	vsfusbh_register_driver(&usrapp.usbh, &vsfusbh_hub_drv);

	app->jeg.nes.rom.buffer = (uint8_t *)APPCFG_ROM_ADDR;
	app->jeg.nes.rom.size = APPCFG_ROM_SIZE;
	vsfjeg_init(&app->jeg);

	if (app_hwcfg.usbd.pullup.port != VSFHAL_DUMMY_PORT)
	{
		vsfhal_gpio_init(app_hwcfg.usbd.pullup.port);
		vsfhal_gpio_clear(app_hwcfg.usbd.pullup.port, 1 << app_hwcfg.usbd.pullup.pin);
		vsfhal_gpio_config(app_hwcfg.usbd.pullup.port, app_hwcfg.usbd.pullup.pin, VSFHAL_GPIO_OUTPP);
	}
	app->usbd.device.drv->disconnect();

	vsftimer_create_cb(200, 1, usrapp_usbd_conn, app);
}

void usrapp_srt_poll(struct usrapp_t *app){}

void usrapp_nrt_init(struct usrapp_t *app){}

void usrapp_nrt_poll(struct usrapp_t *app)
{
	vsfjeg_poll(&app->jeg);
}

bool usrapp_cansleep(struct usrapp_t *app)
{
	return false;
}
